饥荒联机版Mod开发 您所在的位置:网站首页 饥荒single player health mod 饥荒联机版Mod开发

饥荒联机版Mod开发

2024-07-18 04:06| 来源: 网络整理| 查看: 265

饥荒联机版Mod开发——常用inst方法(八) 前言生成实体监听/推送事件使用标签增删组件网络组件 定时/阶段任务回调函数父子实体平台删除位置,角度显示/隐藏判断大脑状态图皮肤地图物理传送门

前言

在饥荒联机版Mod开发——Class, Prefab, component,debug(四) 中我们简单介绍了游戏实体(Entity)和组件的使用方法,在这一章里我们将学习Entity的常用方法,想了解更多也可以去看文中提到的源代码

生成实体

生成实体可分为两种情况,一种是控制台生成,另一种是代码里生成。前者是我们进游戏后调试用,后者是日常写代码时用。

控制台生成预设物实体

--consolecommands.lua --在鼠标位置生成预设物实体,如猪人 "pigman" function c_spawn(prefab, count=1) ... end --给予玩家可放进物品栏的预设物实体,如树枝 "twigs" function c_give(prefab, count=1) ... end

代码里生成预设物实体、实体

--mainfunctions.lua --生成并返回预设物实体,一般只需要个name。如果creator没有对应皮肤,则显示默认皮肤 function SpawnPrefab(name, skin, skin_id, creator) ... end --在原本的实体位置生成预设物实体,并删除原本的实体,一般只需要前两个参数 function ReplacePrefab(original_inst, name, skin, skin_id, creator) ... end --生成并返回实体,name可省,常见于各prefab的fn中 function CreateEntity(name) local ent = TheSim:CreateEntity() --C层的Entity数据,存放在inst.entity local guid = ent:GetGUID() --GUID独一无二的标志数字 local scr = EntityScript(ent) --entityscript.lua,这就是inst if name ~= nil then scr.name = name end Ents[guid] = scr --全部Entity都放到Ents这个全局表里(声明在main.lua) NumEnts = NumEnts + 1 --全部Entity的数量(声明在main.lua) return scr --返回inst end

从CreateEntity中我们可以看成,常见的 inst:XXX() 实际调用的是entityscript.lua中的方法

此外mainfunctions.lua中还定义了其他的全局函数,如 获取时间的GetTick()、 游戏开始时的Start()、保存游戏SaveGame(…),关闭游戏Shutdown()、SaveAndShutdown() 游戏回调OnFocusLost()、OnFocusGained()、 Entity回调OnEntitySleep(),OnEntityWake,OnPhysicsWake,OnPhysicsSleep、 GUID处理函数GetEntityString等; inst.GUID

监听/推送事件

事件作为脚本中传递信息的重要途径,在降低代码复杂度,提供可扩展型上发挥着至关重要的作用。本质是把函数(fn)先存起来(ListenForEvent),在特定的时候(PushEvent)再传入参数(source, data)来调用函数(fn)。

--entityscript.lua --监听/取消监听事件 --event:string,独一无二的事件名,对应inst:PushEvent中的event --fn:监听的函数,参数是 (source, data),source对应调用PushEvent的Entity,data对应PushEvent时的data,也可以print出来看看data是什么 --source:被监听的实体,默认监听自身 inst:ListenForEvent(event, fn, source=inst) inst:RemoveEventCallback(event, fn, source=inst) inst:RemoveAllEventCallbacks() --推送事件,运行fn --event:string,独一无二的事件名,mod的event推荐加个前缀 --data:any,一般是个table --如果inst有sg(stategraph状态图)且sg在监听这个事件,也会调用 inst.sg:PushEvent(event, data) --如果inst有brain(大脑/AI)也会调用 inst.brain:PushEvent(event, data) inst:PushEvent(event, data) --table的key可以是任意非nil值,通过table[key]来访问,key是字符串时可以写成table.key --当前source被监听的事件列表 source.event_listeners[event][inst] = {fn1, fn2, ...} --当前inst在监听的事件列表 inst.event_listening[event][source] = {fn1, fn2, ...}

在监听世界(TheWorld)时,可以把source赋值为TheWorld。不过如果要监听世界的状态(worldstate)(时间,洞穴时间,月圆,天气,季节,温度,湿度,下雪,下雨等) 更推荐使用下面的函数

--entityscript.lua --TheWorld的预设物在prefabs/world.lua --监听/取消监听世界TheWorld.state中的状态 (components/worldstate) --var:components/worldstate.lua中 SetVariable(var, val, togglename) --fn:一般参数为(inst, val),不过start或stop开头的var,只有inst。 inst:WatchWorldState(var, fn) inst:StopWatchingWorldState(var, fn) inst:StopAllWatchingWorldStates() --inst.worldstatewatching[var] = {fn1, fn2, ...} --_watchers[var][inst] = { {fn, inst}, ... } --举个例子components/worldstate.lua中 --SetVariable(var, val, togglename) --有第三个参数时,有start和stop开头var (val and "start" or "stop")..togglename SetVariable("cavephase", phase) --> var="cavephase", fn的val=phase,洞穴白天到黄昏等时期变化时推送 SetVariable("iscaveday", phase == "day", "caveday") --> var="iscaveday" fn第二个参数val=(phase == "day"),一个boolean --> var="startcaveday",val为true时推送,fn参数只有inst --> var="stopcavaday",val为false时推送,fn参数只有inst

注:

TheWorld是Entity,赋值在prefabs/world.lua,负责管理世界、地图、时钟、玩家生成等。判断是否是地下世界,TheWorld:HasTag(“cave”)The开头及Ents等全局变量(声明在main.lua),可以在游戏中直接访问到在modmain中也可以访问到main中的全局变量,不过直接访问时可能还没赋值,所以一般写在 AddGamePostInit(fn()) --先 AddSimPostInit(fn()) --后,和单机不同,fn没有参数,ThePlayer也没赋值 如果需要访问ThePlayer,用下面函数 AddPlayerPostInit(fn(player)) --在fn中用ThePlayer==player来判断是否当前玩家 使用标签

标签常用于Component判断Entity,也影响着生物的AI等。 _开头的标签比较特殊,在下面的组件部分介绍。 常见的tags如下:

NOCLICK:不可点击物体FX:特效lightningtarget:闪电目标(移除就不不会被雷劈)electricdamageimmune:电伤害免疫(机器人免疫电伤害)irreplaceable:无法替代,玩家离开世界自动掉落(眼骨等)player、playerghost:玩家状态(也可以用inst.components.health:IsDead()来判断)scarytoprey:吓跑小动物(移除就不会吓跑小动物) --tag: string inst:AddTag(tag) inst:RemoveTag(tag) inst:HasTag(tag) --tags = {"tag1", "tag2", ...} inst:HasTags(tags) --拥有全部 inst:HasOneOfTags(tags) --拥有其中一个标签 增删组件 --name:string, 对应components中文件夹中的文件名,组件的使用看第四章 inst:AddComponent(name) --有_replica的同名组件会自动添加(服务器) inst:RemoveComponent(name) --inst.components.name --inst.repica.name 网络组件

以_replica的结尾同名组件 ,用网络变量(netvars.lua)进行服务器和客户端之间的通信,需要服务器和客户端都添加以_replica的结尾同名组件。 这里先讲下添加组件流程,具体使用以后出个网络通信相关的教程,感兴趣的也可以先去看 官方的netvars教程 注:动作的网络变量了entityscript.lua的AddNetwork(),其他的大部分在player_classified.lua

以玩家的health组件为例子(服务器修改血量,通知)

local inst = CreateEntity() inst.entity:AddNetwork() --网络通信当然要添加网络了 inst:AddTag("_health") --添加网络组件标签 inst.entity:SetPristine() --客户端根据添加的_开头的tag,添加xxx_replica组件 if not TheWorld.ismastersim then return inst end --客户端返回,下面是仅服务器代码 inst:RemoveTag("_health") --移除网络组件标签,等AddComponent会再次加上。官方书写习惯,不删问题应该也不大 inst:AddComponent("health") --服务器添加health和health_replica组件,和_health标签。 --注:客户端只需要知道玩家的血量,其他生物的血量并不需要知道,所以其他生物的预设物fn中 --并不会有inst:AddTag("_health") 和 inst:RemoveTag("_health") 这两句 定时/阶段任务 --entityscript.lua scheduler.lua --下面4个函数的返回值都是Periodic 可通过 :Cancel() 取消任务 --定时任务,time秒后调用 fn(inst, ...)。仅调用一次,不需要手动取消 --注:inst:DoTaskInTime(0,...) 时会在下一帧调用; Sprite中是毫秒 inst:DoTaskInTime(time, fn, ...) --阶段任务,initialdelay秒后调用fn(inst, ...),之后每隔time秒调用一次fn,需要手动取消 --一般都是 inst.task = inst:DoPeriodicTask(...),某个时候 inst.task:Cancel() inst:DoPeriodicTask(time, fn, initialdelay=time, ...) --和上面的类似,不过定时不受世界暂停时影响 inst:DoStaticTaskInTime(time, fn, ...) inst:DoStaticPeriodicTask(time, fn, initialdelay, ...) inst:CancelAllPendingTasks() --取消上面所以的定时任务 回调函数

-------- entityscript.lua--------- Fn:对应函数 XXXCb:回调函数,非nil时调用 一般都是先调用ComponentCb,之后再调用EntityCb,除非是Pre开头的 EntityCb常规写法: inst.OnXXX = function(inst, …) … end ComponentCb常规写法:function Cmpname:OnXXX(…) … end

refs:引用的Entity列表或nil,{Entity1, Entity2, …}cmpdata:一般是个table,仅保存数值,不保存引用newents:重新开服时生成的Entitys,一般用不上builder:一般是玩家的Entitydt:deltatime,距离上次更新的时间 FnEntityCbComponentCbinst:GetPersistData()inst:OnSave(data) -> refscmp:OnSave() -> cmpdata, refsinst:SetPersistData(data, newents)inst:OnPreLoad(data, newents) inst:OnLoad(data, newents)cmp:OnLoad(cmpdata, newents)inst:LoadPostPass(newents, savedata)inst:OnLoadPostPass(newents, savedata)cmp:LoadPostPass(newents, cmpdata)inst:OnBuilt(builder)inst:OnBuiltFn(builder)cmp:OnBuilt(builder)inst:Remove()inst:OnRemoveEntity()cmp:OnRemoveEntity()inst:LongUpdate(dt)inst:OnLongUpdate(dt)cmp:LongUpdate(dt)

--------Update:update.lua-------- dt: deltatime,距离上次更新的时间

WallUpdate:墙壁更新Update:日常更新StaticUpdate:世界暂停时开始更新LongUpdate:长时间间隔的更新,如进出洞穴,跳天数等 FnEntityCbComponentCbWallUpdate(dt)-cmp:OnWallUpdate(dt)Update(dt)-cmp:OnUpdate(dt)StaticUpdate(dt)-cmp:OnStaticUpdate(0)LongUpdate(dt, ignore_player)inst:OnLongUpdate(dt)cmp:LongUpdate(dt)

--------Player:player_common.lua-----------. 玩家特有回调 inst.OnNewSpawn = function() … end inst.OnDespawn = function() … end

父子实体 --entityscript.lua --child:Entity inst:AddChild(child) inst:RemoveChild(child) --inst.children[child] = true --child.parent = inst child.platform = nil 平台

当前只用在船上,让玩家随船移动

--entityscript.lua --child: Entity inst:AddPlatformFollower(child) inst:RemovePlatformFollower(child) inst:GetPlatformFollowers() inst:GetCurrentPlatform() --inst.platformfollowers[child] = true --child.platform = inst child.parent = nil 删除 --移除自身和子Entity,从父Entity的孩子中移除 --触发事件onremove,OnRemoveEntity回调 --移除所以监听、组件、定时和阶段任务 inst:Remove() inst:IsValid() --正常返true,被Remove返false 位置,角度 inst:GetPosition() --> Point(inst.Transform:GetWorldPosition()) 一个table inst:GetRotation() --> inst.Transform:GetRotation() inst:FacePoint(x, y, z) --> 面向某点 inst:ForceFacePoint(x, y, z) --> 强制面向某点 inst:FaceAwayFromPoint(dest, force) --DistanceSq: 距离的平方,少了sqrt,减少计算量 inst:GetDistanceSqToInst(inst) --> 距离的平方 inst:IsNear(otherinst, dist) --> inst:GetDistanceSqToInst(otherinst) < dist*dist inst:GetDistanceSqToPoint(x, y, z) --> 距离的平方 inst:IsNearPlayer(range, isalive) --> range半径内是否有玩家 inst:GetNearestPlayer(isalive) --> 最近的玩家 inst:GetDistanceSqToClosestPlayer(isalive) --> 距离最近的玩家的距离的平方 显示/隐藏 --显示/隐藏,仅改变显示 inst:Show() inst:Hide() --显示到屏幕,常用于物品被移出物品栏 --IsInLimbo()->false inst:Show() --开始brain,sg,Physics,Light,AnimState,DynamicShadow,MiniMapEntity --inst:PushEvent("exitlimbo") inst:ReturnToScene() --从屏幕隐藏中,常用物品被放进物品栏 --IsInLimbo()->true inst:Hide() --停止brain,sg,Physics,Light,AnimState,DynamicShadow,MiniMapEntity --inst:PushEvent("enterlimbo") inst::RemoveFromScene() 判断 inst:IsOnWater() --是否在水上,即没船且脚下是海 inst:IsInLimbo() --是否在物品栏,仅在mastersim(服务器)有效,客户端用INLIMBO标签判断? inst:GetIsWet() --是否是湿的,服务器客户端都可以使用 inst:IsInLight() --是否在光照内 inst:IsValid() --正常返true,被Remove返false 大脑 --brainfn: require("brains/xxxbrain") inst:SetBrain(brainfn) inst:StopBrain() inst:RestartBrain() 状态图 --name: "SGXXX" stategraphs文件夹下的文件名 inst:SetStateGraph(name) inst:ClearStateGraph() 皮肤 --entityscript.lua inst:GetSkinBuild() inst:GetSkinName() --inst.AnimState:SetSkin(build_name, def_build) 地图 --entityscript.lua --设置参数,具体应用在components/map.lua inst:SetDeployExtraSpacing(spacing) inst:SetTerraformExtraSpacing(spacing) inst:SetGroundTargetBlockerRadius(radius) inst:IsOnValidGround() --是否在地面上 inst:IsOnOcean(allow_boats) --是否在海上 inst:IsOnPassablePoint(include_water, floating_platforms_are_not_passable) --是否可通过某点 inst:GetCurrentTileType() --获得当前地皮,如果要获得有效地皮先判断inst:IsOnValidGround() 物理

设置物理的时候,一般用的是 standardcomponents.lua 里的 MakeXXXPhysics,不过偶尔有需要重新设置物理碰撞半径的时候

--entityscript.lua inst:SetPhysicsRadiusOverride(radius) --设置覆盖的物理碰撞半径 inst:GetPhysicsRadius(default) --返回物理碰撞半径或default 传送门

→饥荒联机版Mod开发——制作栏(九) ←饥荒联机版Mod开发——制作可入锅,烹饪,凉干的食物(七)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有